package ru.usu.gv.core;

import com.sun.j3d.utils.behaviors.mouse.MouseRotate;
import com.sun.j3d.utils.behaviors.mouse.MouseTranslate;
import com.sun.j3d.utils.behaviors.mouse.MouseWheelZoom;
import com.sun.j3d.utils.behaviors.mouse.MouseZoom;

import ru.usu.gv.gui.analysis.AngleEdgeAnalyzer;
import ru.usu.gv.gui.analysis.BendAnalyzer;
import ru.usu.gv.gui.analysis.CommonStatisticsAnalyzer;
import ru.usu.gv.gui.analysis.CrossingAnalyzer;
import ru.usu.gv.gui.analysis.OrtogonalAnalyzer;
import ru.usu.gv.gui.analysis.UniformEdgeAnalyzer;
import ru.usu.gv.gui.analysis.VertexDistanceAnalyzer;
import ru.usu.gv.gui.analysis.VolumeAnalyzer;
import ru.usu.gv.gui.behavior.DragEdgeMouseBehavior;
import ru.usu.gv.gui.behavior.GVKeyNavigatorBehavior;
import ru.usu.gv.gui.behavior.GraphAnalysBehavior;
import ru.usu.gv.gui.behavior.GraphLayoutBehavior;
import ru.usu.gv.gui.behavior.OfflineDynamicVisBehavior;
import ru.usu.gv.gui.behavior.OnlineDynamicVisBehavior;
import ru.usu.gv.gui.behavior.RotationBehavior;
import ru.usu.gv.gui.layout.ILayout;
import ru.usu.gv.uicomponents.UIEdge;
import ru.usu.gv.uicomponents.UIVertex;
import ru.usu.gv.uicomponents.listener.GraphChangeListenerColection;
import ru.usu.gv.uicomponents.listener.IChangeListener;
import ru.usu.gv.utils.generators.BAGenerator;
import ru.usu.gv.utils.generators.BinaryTreeGenerator;
import ru.usu.gv.utils.generators.GraphGeneratorManager;
import ru.usu.gv.utils.generators.Grid2DGenerator;
import ru.usu.gv.utils.generators.Grid3DGenerator;
import ru.usu.gv.utils.generators.MobiusGenerator;
import ru.usu.gv.utils.generators.RandomGenerator;
import ru.usu.gv.utils.generators.TreeGenerator;
import ru.usu.gv.view.DragEdgePickingClient;
import ru.usu.gv.view.DragVertexPickingClient;
import ru.usu.gv.view.GraphPickBehavior;
import ru.usu.gv.view.PickListener;
import ru.usu.gv.view.PopupEdgePickingClient;
import ru.usu.gv.view.PopupVertexPickingClient;

import javax.media.j3d.Behavior;
import javax.media.j3d.BoundingSphere;
import javax.media.j3d.TransformGroup;

/**
 @author: Sergey Pupyrev 
 Apr 2, 2006
 */

public class GraphEngine
{
	private static GraphEngine INSTANCE = new GraphEngine();

	private GraphLayoutBehavior graphLayoutBehavior;
	private OnlineDynamicVisBehavior onlineDynamicVisBehavior;
	private OfflineDynamicVisBehavior offlineDynamicVisBehavior;
	private DragEdgeMouseBehavior dragMouseBehavior;
	private GraphPickBehavior graphPickBehavior;
	private GraphAnalysBehavior analysBehavior;
	private GraphAnalysBehavior analysUIBehavior;
	private MouseRotate mouseRotationBehavior;
	private MouseZoom mouseZoomBehavior;
	private MouseWheelZoom mouseWheelZoomBehavior;
	private MouseTranslate mouseTranslateBehavior;
	private Behavior keyNavigatorBehavior;
	private RotationBehavior rotationBehavior;

	private PickListener pickListener = new PickListener();

	private GraphChangeListenerColection listeners = new GraphChangeListenerColection();
	private GraphChangeListenerColection uiListeners = new GraphChangeListenerColection();

	private GraphEngine()
	{
	}

	public static GraphEngine getInstance()
	{
		return INSTANCE;
	}

	public void init()
	{
		GraphCanvas graphCanvas = GraphCanvas.getInstance();
		BoundingSphere bounds = graphCanvas.getBoundingSphere();

		graphLayoutBehavior = new GraphLayoutBehavior(bounds);
		graphCanvas.addCustomBehavior(graphLayoutBehavior);

		offlineDynamicVisBehavior = new OfflineDynamicVisBehavior(bounds);
		graphCanvas.addCustomBehavior(offlineDynamicVisBehavior);
		onlineDynamicVisBehavior = new OnlineDynamicVisBehavior(bounds);
		graphCanvas.addCustomBehavior(onlineDynamicVisBehavior);
		//TODO: why too many j3dMessage's???
		//		rotationBehavior = new RotationBehavior(graphCanvas.getObjRoot(), graphCanvas.getTransformGroup(), bounds);
		//		graphCanvas.addCustomBehavior(rotationBehavior);

		keyNavigatorBehavior = new GVKeyNavigatorBehavior(graphCanvas.getTransformGroup());
		graphCanvas.addCustomBehavior(keyNavigatorBehavior);
		keyNavigatorBehavior.setEnable(true);

		dragMouseBehavior = new DragEdgeMouseBehavior(graphCanvas.getTransformGroup());
		graphCanvas.addCustomBehavior(dragMouseBehavior);
		graphPickBehavior = new GraphPickBehavior(graphCanvas, graphCanvas.getObjRoot(), bounds);
		graphCanvas.addCustomBehavior(graphPickBehavior);
		graphPickBehavior.setEnable(true);

		initMouseBehavior();

		initAnalysBehavior();

		initPickListener();

		initGenerators();
	}

	private void initMouseBehavior()
	{
		GraphCanvas graphCanvas = GraphCanvas.getInstance();
		TransformGroup transformGroup = graphCanvas.getTransformGroup();

		mouseRotationBehavior = new MouseRotate(transformGroup);
		graphCanvas.addCustomBehavior(mouseRotationBehavior);

		mouseZoomBehavior = new MouseZoom(transformGroup);
		graphCanvas.addCustomBehavior(mouseZoomBehavior);

		mouseWheelZoomBehavior = new MouseWheelZoom(transformGroup);
		graphCanvas.addCustomBehavior(mouseWheelZoomBehavior);

		mouseTranslateBehavior = new MouseTranslate(transformGroup);
		graphCanvas.addCustomBehavior(mouseTranslateBehavior);
	}

	private void initPickListener()
	{
		pickListener.setPickedListLimit(1);
		pickListener.register(UIVertex.class, new DragVertexPickingClient());
		pickListener.register(UIVertex.class, new PopupVertexPickingClient());
		pickListener.register(UIEdge.class, new DragEdgePickingClient());
		pickListener.register(UIEdge.class, new PopupEdgePickingClient());
	}

	private void initGenerators()
	{
		//generators
		GraphGeneratorManager.getInstance().addGenerator(new Grid2DGenerator());
		GraphGeneratorManager.getInstance().addGenerator(new Grid3DGenerator());
		GraphGeneratorManager.getInstance().addGenerator(new TreeGenerator());
		GraphGeneratorManager.getInstance().addGenerator(new BinaryTreeGenerator());
		GraphGeneratorManager.getInstance().addGenerator(new RandomGenerator());
		GraphGeneratorManager.getInstance().addGenerator(new MobiusGenerator());
		GraphGeneratorManager.getInstance().addGenerator(new BAGenerator());
	}

	private void initAnalysBehavior()
	{
		analysBehavior = new GraphAnalysBehavior(GraphCanvas.getInstance().getBoundingSphere());
		GraphCanvas.getInstance().addCustomBehavior(analysBehavior);
		analysBehavior.addAnalyzer(new CommonStatisticsAnalyzer());

		getGraphListeners().addListener(new IChangeListener()
		{
			public void doChange(Object sender)
			{
				analysBehavior.setNeedRecalculate(true);
				analysBehavior.setEnabledInternal();
			}
		});

		//////////////////
		analysUIBehavior = new GraphAnalysBehavior(GraphCanvas.getInstance().getBoundingSphere());
		GraphCanvas.getInstance().addCustomBehavior(analysUIBehavior);
		analysUIBehavior.addAnalyzer(new UniformEdgeAnalyzer());
		analysUIBehavior.addAnalyzer(new VertexDistanceAnalyzer());
		analysUIBehavior.addAnalyzer(new BendAnalyzer());
		analysUIBehavior.addAnalyzer(new AngleEdgeAnalyzer());
		analysUIBehavior.addAnalyzer(new VolumeAnalyzer());
		analysUIBehavior.addAnalyzer(new OrtogonalAnalyzer());
		analysUIBehavior.addAnalyzer(new CrossingAnalyzer());

		getUIGraphListeners().addListener(new IChangeListener()
		{
			public void doChange(Object sender)
			{
				analysUIBehavior.setNeedRecalculate(true);
				analysUIBehavior.setEnabledInternal();
			}
		});
	}

	public void startLayout(ILayout layout)
	{
		graphLayoutBehavior.setLayout(layout);
		graphLayoutBehavior.setEnable(true);
	}

	public void setEnableGraphEdit(boolean enabled)
	{
		mouseRotationBehavior.setEnable(enabled);
		mouseZoomBehavior.setEnable(enabled);
		mouseWheelZoomBehavior.setEnable(enabled);
		mouseTranslateBehavior.setEnable(enabled);
	}

	public MouseRotate getMouseRotationBehavior()
	{
		return mouseRotationBehavior;
	}

	public DragEdgeMouseBehavior getMouseBehavior()
	{
		return dragMouseBehavior;
	}

	public GraphAnalysBehavior getAnalysBehavior()
	{
		return analysBehavior;
	}

	public GraphAnalysBehavior getAnalysUIBehavior()
	{
		return analysUIBehavior;
	}

	public OfflineDynamicVisBehavior getOfflineDynamicVisBehavior()
	{
		return offlineDynamicVisBehavior;
	}

	public OnlineDynamicVisBehavior getOnlineDynamicVisBehavior()
	{
		return onlineDynamicVisBehavior;
	}

	public GraphChangeListenerColection getGraphListeners()
	{
		return listeners;
	}

	public GraphChangeListenerColection getUIGraphListeners()
	{
		return uiListeners;
	}

	private boolean auoRotationToggled = false;

	public void toggleRotator()
	{
		if (auoRotationToggled == false)
		{
			rotationBehavior.setEnable(true);
			rotationBehavior.defaultRotate();
			auoRotationToggled = true;
		}
		else
		{
			rotationBehavior.setEnable(false);
			rotationBehavior.getRotator().setEnable(false);
			auoRotationToggled = false;
		}
	}

	public PickListener getPickListener()
	{
		return pickListener;
	}

}
